package com.clp.protocol.iec104.apdu.asdu.infoobj;

import com.clp.protocol.iec104.apdu.asdu.infoobj.infoelem.InfoElem;
import com.clp.protocol.iec104.apdu.asdu.infoobj.qua.Qua;
import com.clp.protocol.iec104.definition.TypeTag;
import com.clp.protocol.core.pdu.nbytepdu.NBytePduClip;
import com.clp.protocol.core.pdu.ByteToStringFormat;
import com.clp.protocol.core.pdu.nbytepdu.time2a.CP56Time2a;
import com.clp.protocol.core.pdu.nbytepdu.time2a.Time2a;
import com.sun.istack.internal.NotNull;
import io.netty.buffer.ByteBuf;
import lombok.Getter;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 地址连续的信息体对象
 */
@Getter
public class CInfoObj extends InfoObj implements Comparable<CInfoObj> {
    /**
     * 信息元素地址（3字节）
     */
    private int addr;
    /**
     * 信息内容
     */
    private InfoElem infoElem;
    /**
     * 限定词（如果是遥测限定词会保存品质描述词）
     */
    private Qua qua;
    /**
     * 时标
     */
    private CP56Time2a time2a;

    public CInfoObj(TypeTag typeTag, int addr) {
        this(typeTag, addr, null, null, null);
    }

    public CInfoObj(TypeTag typeTag, int addr, InfoElem infoElem, Qua qua, CP56Time2a time2a) {
        super(typeTag);
        this.addr = addr;
        this.infoElem = infoElem;
        this.qua = qua;
        this.time2a = time2a;
    }

    @Override
    public CInfoObj refreshFrom(ByteBuf buf) {
        checkTypeTag();
        TypeTag typeTag = typeTag();
        // 1、设置信息元素地址（已经在构造时赋予了）
        // 2、设置信息元素内容
        if (typeTag.hasInfoElem()) {
            this.infoElem = typeTag.newInvalidInfoElem().refreshFrom(buf);
        }
        // 3、设定限定词（如果有）
        if (typeTag.hasQua()) {
            this.qua = typeTag.newInvalidQua().refreshFrom(buf);
        }
        // 4、设定时标（如果有）
        if (typeTag.hasTime2a()) {
            this.time2a = Time2a.newInvalidCP56Time2a().refreshFrom(buf);
        }
        return this;
    }

    @Override
    public boolean isValid() {
        if (!hasTypeTag()) return false;
        if (addr < 0) return false;
        if (typeTag().hasInfoElem() && (infoElem == null || !infoElem.isValid())) return false;
        if (typeTag().hasQua() && (qua == null || !qua.isValid())) return false;
        return !typeTag().hasTime2a() || (time2a != null && time2a.isValid());
    }

    @Override
    public void writeBytesTo(ByteBuf buf) {
        TypeTag typeTag = typeTag();
        // 连续的信息体不需要写入地址值
        // 写入信息元素内容（如果有）
        if (typeTag.hasInfoElem()) {
            infoElem.writeBytesTo(buf);
        }
        // 写入限定词的值（如果有）
        if (typeTag.hasQua()) {
            qua.writeBytesTo(buf);
        }
        // 获取时标（如果有）
        if (typeTag.hasTime2a()) {
            time2a.writeBytesTo(buf);
        }
    }

    @Override
    public void writeFormattedByteStringsTo(StringBuilder sb, String frameClipBytesSeparator, String byteSeparator, ByteToStringFormat byteFormat) {
        TypeTag typeTag = typeTag();
        List<NBytePduClip<?>> childClips = new ArrayList<>();
        if (typeTag.hasInfoElem()) {
            childClips.add(infoElem);
        }
        if (typeTag.hasQua()) {
            childClips.add(qua);
        }
        if (typeTag.hasTime2a()) {
            childClips.add(time2a);
        }
        if (childClips.isEmpty()) return;
        childClips.get(0).writeFormattedByteStringsTo(sb, frameClipBytesSeparator, byteSeparator, byteFormat);
        for (int i = 1; i < childClips.size(); i++) {
            sb.append(frameClipBytesSeparator);
            childClips.get(i).writeFormattedByteStringsTo(sb, frameClipBytesSeparator, byteSeparator, byteFormat);
        }
    }

    @Override
    public void writeSimpleDescriptionTo(StringBuilder sb) {
        sb.append("addr：").append(addr);
        if (typeTag().hasInfoElem()) {
            sb.append(", infoElem：");
            infoElem.writeSimpleDescriptionTo(sb);
        }
        if (typeTag().hasQua()) {
            sb.append(", qua：");
            qua.writeSimpleDescriptionTo(sb);
        }
        if (typeTag().hasTime2a()) {
            sb.append(", time2a：");
            time2a.writeSimpleDescriptionTo(sb);
        }
    }

    @Override
    public void writeDetailDescriptionTo(StringBuilder sb) {
        sb.append("信息体地址：").append(addr);
        if (typeTag().hasInfoElem()) {
            sb.append(", 信息内容：");
            infoElem.writeDetailDescriptionTo(sb);
        }
        if (typeTag().hasQua()) {
            sb.append(", 限定词：");
            qua.writeDetailDescriptionTo(sb);
        }
        if (typeTag().hasTime2a()) {
            sb.append(", 时标：");
            time2a.writeDetailDescriptionTo(sb);
        }
    }

    @Override
    public int compareTo(@NotNull CInfoObj o) {
        return this.addr - o.addr;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        CInfoObj cInfoObj = (CInfoObj) o;
        return addr == cInfoObj.addr && Objects.equals(infoElem, cInfoObj.infoElem) && Objects.equals(qua, cInfoObj.qua) && Objects.equals(time2a, cInfoObj.time2a);
    }

    @Override
    public int hashCode() {
        return Objects.hash(addr, infoElem, qua, time2a);
    }
}
